﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinFu.IoC.Configuration;
using LinFu.IoC;
using LinFu.IoC.Interfaces;

namespace CoreEx.Database.Query.Implementation
{
    [Implements(typeof(IExpressionFactory))]
    public class ExpressionFactory : IExpressionFactory, IInitialize
    {
        private IServiceContainer _serviceContainer;
                                                
        public ExpressionBase Column(string columnName)
        {
            return Column(string.Empty, columnName);
        }

        public ExpressionBase Column(string columnPrefix, string columnName)
        {
            var columnExpression = _serviceContainer.GetService<IColumnExpression>();
            columnExpression.ColumnPrefix = columnPrefix;
            columnExpression.ColumnName = columnName;
            return (ExpressionBase) columnExpression;
        }

        public ExpressionBase Table(string tableName)
        {
            var tableExpression = _serviceContainer.GetService<ITableExpression>();
            tableExpression.TableName = tableName;
            return (ExpressionBase) tableExpression;
        }

        public ExpressionBase Table(string tableName, string alias)
        {
            return Alias(Table(tableName), alias);
        }


        public ExpressionBase OrderBy(IExpression target)
        {
            return OrderBy(target, SortDirection.Ascending);
        }

        public ExpressionBase OrderBy(IExpression target, SortDirection direction)
        {
            var orderByExpression = _serviceContainer.GetService<IOrderByExpression>();
            orderByExpression.Target = target;
            orderByExpression.Direction = direction;
            return (ExpressionBase) orderByExpression;
        }

        public ExpressionBase Constant(object value)
        {
            var constantExpression = _serviceContainer.GetService<IConstantExpression>();
            constantExpression.Value = value;
            return (ExpressionBase)constantExpression;
        }

        public ExpressionBase Eq(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.Equal);
        }

        public ExpressionBase GreaterThan(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.GreaterThan);
        }


        public ExpressionBase GreaterOrEqual(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.GreaterOrEqual);
        }

        public ExpressionBase LessThan(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.LessThan);
        }


        public ExpressionBase LessOrEqual(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.LessThanOrEqual);
        }


        public ExpressionBase   StartsWith(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.StartsWith);            
        }

        public ExpressionBase Contains(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.Contains);            
        }

        public ExpressionBase EndsWith(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.EndsWith);            
        }

        public ExpressionBase Or(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.Or);
        }

        public ExpressionBase And(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.And);
        }

        public ExpressionBase Count()
        {
            return Count(null);
        }

        public ExpressionBase Count(IExpression target)
        {
            return CreateAggregateExpression(target, AggregateExpressionType.Count);            
        }


        public ExpressionBase Sum(IExpression target)
        {
            return CreateAggregateExpression(target, AggregateExpressionType.Sum);
        }





        public ExpressionBase Add(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.Add);
        }

        public ExpressionBase Subtract(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.Subtract);
        }

        public ExpressionBase Multiply(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.Multiply);
        }

        public ExpressionBase Divide(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateBinaryExpression(leftExpression, rightExpression, BinaryExpressionType.Divide);
        }

        public SelectQueryExpression Select(IExpression projection)
        {
            var query = new SelectQueryExpression(projection, this);             
            ((ISelectExpression) query).IsSubQuery = true;
            return query;
        }

        public SelectQueryExpression Select(Func<IExpressionFactory, IExpression> expression)
        {
            return Select(expression(this));
        }

        public ExpressionBase Alias(IExpression target, string alias)
        {
            IAliasExpression aliasExpression = _serviceContainer.GetService<IAliasExpression>();
            aliasExpression.Target = target;
            aliasExpression.Alias = alias;
            return (ExpressionBase) aliasExpression;
        }

        public ExpressionBase Alias(Func<IExpressionFactory, IExpression> target, string alias)
        {
            return Alias(target(this), alias);
        }

        public ExpressionBase Exists<T>(T selectQueryExpression) where T : SelectQueryExpression
        {
            IExistsExpression existsExpression = _serviceContainer.GetService<IExistsExpression>();
            existsExpression.Target = selectQueryExpression;
            return (ExpressionBase) existsExpression;
        }

        public ExpressionBase Exists<T>(Func<IExpressionFactory, T> target) where T : SelectQueryExpression
        {
            return Exists(target(this));
        }

        public ExpressionBase List(IEnumerable<IExpression> source)
        {
            var list = _serviceContainer.GetService<IExpressionList>();
            list.AddRange(source);
            return (ExpressionBase)list;
        }

        public ExpressionBase Composite(IExpression leftExpression, IExpression rightExpression)
        {
            var expression = _serviceContainer.GetService<ICompositeExpression>();
            expression.LeftExpression = leftExpression;
            expression.RightExpression = rightExpression;
            return (ExpressionBase) expression;
        }


        public ExpressionBase Not(IExpression operand)
        {
            var expression = _serviceContainer.GetService<IUnaryExpression>();
            expression.Operand = operand;
            expression.ExpressionType = UnaryExpressionType.Not;
            return (ExpressionBase) expression;
        }

        public ICaseExpression Case()
        {
            return _serviceContainer.GetService<ICaseExpression>();            
        }

        public ExpressionBase Join(JoinType joinType, IExpression target, IExpression expression)
        {
            var joinExpression = _serviceContainer.GetService<IJoinExpression>();
            joinExpression.JoinType = joinType;
            joinExpression.Expression = expression;
            joinExpression.Target = target;
            return (ExpressionBase) joinExpression;
        }

        public ExpressionBase Union(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateSetOperatorExpression(leftExpression, rightExpression, SetOperatorExpressionType.Union);
        }

        public ExpressionBase UnionAll(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateSetOperatorExpression(leftExpression, rightExpression, SetOperatorExpressionType.UnionAll);
        }

        public ExpressionBase Intersect(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateSetOperatorExpression(leftExpression, rightExpression, SetOperatorExpressionType.Intersect);
        }

        public ExpressionBase Except(IExpression leftExpression, IExpression rightExpression)
        {
            return CreateSetOperatorExpression(leftExpression, rightExpression, SetOperatorExpressionType.Except);
        }

        public ExpressionBase In(IExpression target, object[] values)
        {                        
            var expression = _serviceContainer.GetService<IInExpression>();
            expression.Target = target;
            expression.Values = values;
            return (ExpressionBase) expression;
        }


        private ExpressionBase CreateBinaryExpression(IExpression leftExpression, IExpression rightExpression, BinaryExpressionType expressionType)
        {
            var expression = _serviceContainer.GetService<IBinaryExpression>();
            expression.LeftExpression = leftExpression;
            expression.RightExpression = rightExpression;
            expression.ExpressionType = expressionType;
            return (ExpressionBase)expression;
        }

        private ExpressionBase CreateAggregateExpression(IExpression target,AggregateExpressionType expressionType)
        {
            var expression = _serviceContainer.GetService<IAggregateExpression>();
            expression.Target = target;
            expression.ExpressionType = expressionType;
            return (ExpressionBase) expression;
        }

        private ExpressionBase CreateSetOperatorExpression(IExpression leftExpression,IExpression rightExpression,SetOperatorExpressionType setOperatorExpressionType)
        {
            var expression = _serviceContainer.GetService<ISetOperatorExpression>();
            expression.LeftExpression = leftExpression;
            expression.RightExpression = rightExpression;
            expression.SetOperatorExpressionType = setOperatorExpressionType;
            return (ExpressionBase) expression;
        }



        public void Initialize(IServiceContainer source)
        {
            _serviceContainer = source;
        }
    }
}
